﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.xml;
using iTextSharp.text.pdf;
using SdmxMl.Manager;
using SdmxMl.Structure;
using SdmxMl.Common;
using System.Windows.Forms;



namespace SilcQuest
{
    public class PdfChop
    {
        private SmManager sm;
        private Dictionary<string, CodeListType> dico;
        private Font fontCode;
        private Font fontExclude;
        private Font fontRemark;
        private Font fontExcludeTag;
        private Font fontIncludeTag;
        private Font fontHeader;
        private Font fontFooter;
        private PdfWriter docWriter;
        private Document doc;
        private List<CodeTableRepresentation> codeTableList;

        public PdfChop()
        {
            fontCode = new Font(Font.FontFamily.HELVETICA, 6f, Font.BOLD);
            fontExclude = new Font(Font.FontFamily.HELVETICA, 6f, Font.ITALIC);
            fontRemark = new Font(Font.FontFamily.HELVETICA, 6f, Font.NORMAL);
            fontExcludeTag = new Font(Font.FontFamily.HELVETICA, 4f, Font.NORMAL, BaseColor.WHITE);
            fontIncludeTag = new Font(Font.FontFamily.HELVETICA, 4f, Font.NORMAL, BaseColor.BLACK);
            fontHeader = new Font(Font.FontFamily.TIMES_ROMAN, 7f, Font.NORMAL, BaseColor.BLACK);
            fontFooter = new Font(Font.FontFamily.TIMES_ROMAN, 4f, Font.NORMAL, BaseColor.BLACK);

        }

        private Dictionary<string, CodeListType> GetRefDictionary(HierarchyType hierarchy)
        {
            Dictionary<string, CodeListType> dico = new Dictionary<string, CodeListType>();
            foreach (CodelistRefType crt in hierarchy.CodelistRefList)
            {
                CodeListType clType = null;
                Artefact a = sm.GetArtefact(SdmxArtefactType.CodeLists, crt);
                if (a != null)
                    clType = a as CodeListType;
                if (clType != null)
                    dico.Add(crt.Alias, clType);
            }

            return dico;
        }
        protected void FillRow(string parentCode, CodeRefType crt, int level)
        {
            CodeListType clType = dico[crt.CodelistAliasRef];
            ISdmxHierarchyListItem item = clType.CodeList.LocateItem(crt.CodeID);

            if (item == null)
                throw new ApplicationException("Unable to proceed. Hierarchy referred code Id '" +
                    crt.CodeID + "' is not present in codelist: " + clType.FullIdent);

            CodeType code = item as CodeType;
            PdfPTable tb = CodeInfo(code, level);
            codeTableList.Add(new CodeTableRepresentation(code, tb));

            // Recurse
            foreach (CodeRefType it in crt.CodeRefList)
                FillRow(crt.CodeID, it, level + 1);
        }


        public void TryColumn()
        {
            doc = new Document();

            try
            {
                sm = ReferenceManager.GetSmContainerImage();

                // Ensure in manager
                HierarchicalCodelistType hcl = sm.GetArtefact(SdmxArtefactType.HierarchicalCodelists,
                    new ArtefactRefBase("CH1_SMS+HCL_CHOP+2012.0"), true) as HierarchicalCodelistType;
                ArtefactRefTypedList artl = hcl.ArtefactRefList;
                sm.LoadMissingArtefactsFromReferenceMgr(artl);
                sm.LoadMissingArtefactsFromRegistry(artl);

                dico = GetRefDictionary(hcl.HierarchyList[0]);

                docWriter = PdfWriter.GetInstance(doc, new FileStream("c:\\doc\\chop\\Columns.pdf", FileMode.Create));
                doc.Open();

                //Paragraph heading = new Paragraph("Page Heading", new Font(Font.FontFamily.HELVETICA, 28f, Font.BOLD));
                //heading.SpacingAfter = 18f;
                //doc.Add(heading);

                codeTableList = new List<CodeTableRepresentation>(1000);

                foreach (CodeRefType it in hcl.HierarchyList[0].CodeRefList)
                    FillRow(string.Empty, it, 0);

                // Now write and calculate distribution on two columns
                DistributeCodes();

             //   doc.Add(columns);
            }

            catch (Exception ex)
            {

               MessageBox.Show(ex.Message);

            }

            finally
            {

                doc.Close();

            }
        }

        private void DistributeCodes()
        {
            float headFoot = 30;
            float margin = 32;
            int colIndex = 0;
            float curheight = doc.PageSize.Height - headFoot;

            // First header
            PdfPTable tbHead = HeaderTable(margin, headFoot, 0);
            tbHead.WriteSelectedRows(0, -1, margin, doc.PageSize.Height - 8F, docWriter.DirectContent);

            // distribute in 2 columns
            foreach (CodeTableRepresentation ctb in codeTableList)
            {
                if (curheight - ctb.Table.TotalHeight < headFoot)
                {
                    if (colIndex == 0)
                    {
                        colIndex = 1;
                        curheight = doc.PageSize.Height - headFoot;
                    }
                    else
                    {
                        // Add footer

                        // next page
                        doc.NewPage();

                        // Add header
                        int codeIndex = codeTableList.IndexOf(ctb);
                        tbHead = HeaderTable(margin, headFoot, codeIndex);
                        tbHead.WriteSelectedRows(0, -1, margin, doc.PageSize.Height - 8F, docWriter.DirectContent);

                        colIndex = 0;
                        curheight = doc.PageSize.Height - headFoot;
                    }
                }

                float loc = colIndex == 0 ? margin : margin + doc.PageSize.Width/ 2 ;
                ctb.Table.WriteSelectedRows(0, -1, loc, curheight, docWriter.DirectContent);
                curheight -= ctb.Table.TotalHeight;

            }
        }

        private PdfPTable HeaderTable(float margin, float headFoot, int codeIndex)
        {
            PdfPTable table = new PdfPTable(3);
            float[] widths = new float[] { 8f, 82f, 8f };
            table.TotalWidth = doc.PageSize.Width - 2* margin;
            CodeType codeTop = codeTableList[codeIndex].Code;
            CodeType codeBottom = null;
            int colIndex = 0;
            float curheight = doc.PageSize.Height - headFoot;

            // Locate last in page
            for (int i = codeIndex; i < codeTableList.Count; ++i)
            {
                if (curheight - codeTableList[i].Table.TotalHeight < headFoot)
                {
                    if (colIndex == 0)
                    {
                        colIndex = 1;
                        curheight = doc.PageSize.Height - headFoot;
                    }
                    else
                    {
                        // found first in next page
                        codeBottom = codeTableList[i - 1].Code;
                        break;
                    }
                }
                curheight -= codeTableList[i].Table.TotalHeight;
            }

            if (codeBottom == null)
                codeBottom = codeTableList[codeTableList.Count - 1].Code;

            // 1st cell is ID of codeTop
            Phrase p = new Phrase(BeautifyCodeId(codeTop.Id), fontHeader);
            PdfPCell cell = new PdfPCell(p);
            cell.Border = Rectangle.BOTTOM_BORDER;
            table.AddCell(cell);

            // 2nd cell is to be defined
            p = new Phrase("How to construct is to be defined by CHOP !", fontHeader);
            cell = new PdfPCell(p);
            cell.Border = Rectangle.BOTTOM_BORDER;
            cell.HorizontalAlignment = Element.ALIGN_CENTER;
            table.AddCell(cell);

            // third cell is ID of codeBottom
            p = new Phrase(BeautifyCodeId(codeBottom.Id), fontHeader);
            cell = new PdfPCell(p);
            cell.Border = Rectangle.BOTTOM_BORDER;
            cell.HorizontalAlignment = Element.ALIGN_RIGHT;
            table.AddCell(cell);


            return table;
        }




        private PdfPTable CodeInfo(CodeType c, int level)
        {
            PdfPTable table = new PdfPTable(5);
            float indent = 1f + 10f*level;
            float indentCode = 32f + level * 16f;
            float[] widths = new float[] {indent , indentCode, 80f, 60f, 520f - indent - indentCode};
            table.TotalWidth = 240f;


            table.LockedWidth = true;
            table.SetWidths(widths);
            table.DefaultCell.Border = Rectangle.NO_BORDER;

            // Id and title
            table.AddCell(string.Empty);
            Phrase p = new Phrase(BeautifyCodeId(c.Id), fontCode);
            PdfPCell cell = new PdfPCell(p);
            cell.Border = Rectangle.NO_BORDER;
            table.AddCell(cell);
            string text = c.Name.ToString();
            if (c.AnnotationList.GetAnnotationType("LATERAL") != null)
                text += " [L]";
            p = new Phrase(text, fontCode);
            cell = new PdfPCell(p);
            cell.Colspan = 3;
            cell.Border = Rectangle.NO_BORDER;
            table.AddCell(cell);

            // Exclusion
            AnnotationList anl = c.AnnotationList.GetAllAnnotationOfType("INCLUDE", null);
            bool tag = false;
            foreach (SdmxMl.Common.Annotation a in anl)
            {
                table.AddCell(string.Empty);
                table.AddCell(string.Empty);
                if (tag)
                {
                    table.AddCell(string.Empty);
                }
                else
                {
                    tag = true;
                    cell = new PdfPCell(GenIncludeTag());
                    cell.Border = Rectangle.NO_BORDER;
                    cell.HorizontalAlignment = Element.ALIGN_CENTER;
                    cell.VerticalAlignment = Element.ALIGN_MIDDLE;
                    table.AddCell(cell);
                }
                p = new Phrase(a.Text.ToString(), fontRemark);
                cell = new PdfPCell(p);
                cell.Colspan = 2;
                cell.Border = Rectangle.NO_BORDER;
                table.AddCell(cell);
            }


            // Remarks
            anl = c.AnnotationList.GetAllAnnotationOfType("REMARK", null);
            foreach (SdmxMl.Common.Annotation a in anl)
            {
                table.AddCell(string.Empty);
                table.AddCell(string.Empty);
                p = new Phrase(a.Text.ToString(), fontRemark);
                cell = new PdfPCell(p);
                cell.Colspan = 3;
                cell.Border = Rectangle.NO_BORDER;
                table.AddCell(cell);
            }

            // Hint
            anl = c.AnnotationList.GetAllAnnotationOfType("HINT", null);
            tag = false;
            foreach (SdmxMl.Common.Annotation a in anl)
            {
                table.AddCell(string.Empty);
                table.AddCell(string.Empty);
                if (tag)
                {
                    table.AddCell(string.Empty);
                    table.AddCell(string.Empty);
                }
                else
                {
                    tag = true;
                    p = new Phrase("Kodiere ebenso:", fontRemark);
                    cell = new PdfPCell(p);
                    cell.Colspan = 2;
                    cell.Border = Rectangle.NO_BORDER;
                    table.AddCell(cell);
                }
                p = new Phrase(a.Text.ToString(), fontRemark);
                cell = new PdfPCell(p);
                //cell.Colspan = 3;
                cell.Border = Rectangle.NO_BORDER;
                table.AddCell(cell);
            }

            // Exclusion
            anl = c.AnnotationList.GetAllAnnotationOfType("EXCLUSION", null);
            tag = false;
            foreach (SdmxMl.Common.Annotation a in anl)
            {
                table.AddCell(string.Empty);
                table.AddCell(string.Empty);
                if (tag)
                {
                    table.AddCell(string.Empty);
                }
                else
                {
                    tag = true;
                    cell = new PdfPCell(GenExcludeTag());
                    cell.Border = Rectangle.NO_BORDER;
                    cell.HorizontalAlignment = Element.ALIGN_CENTER;
                    cell.VerticalAlignment = Element.ALIGN_MIDDLE;
                    table.AddCell(cell);
                }
                p = new Phrase(a.Text.ToString(), fontExclude);
                cell = new PdfPCell(p);
                cell.Colspan = 2;
                cell.Border = Rectangle.NO_BORDER;
                table.AddCell(cell);
            }

            // Beachte (NB)
            anl = c.AnnotationList.GetAllAnnotationOfType("NB", null);
            tag = false;
            foreach (SdmxMl.Common.Annotation a in anl)
            {
                table.AddCell(string.Empty);
                table.AddCell(string.Empty);
                if (tag)
                {
                    table.AddCell(string.Empty);
                }
                else
                {
                    tag = true;
                    p = new Phrase("Beachte:", fontRemark);
                    cell = new PdfPCell(p);
                    cell.Border = Rectangle.NO_BORDER;
                    table.AddCell(cell);
                }
                p = new Phrase(a.Text.ToString(), fontRemark);
                cell = new PdfPCell(p);
                cell.Colspan = 2;
                cell.Border = Rectangle.NO_BORDER;
                table.AddCell(cell);
            }


          //  cell.BackgroundColor = BaseColor.BLACK;
          //  cell.Padding = 3;
          //  cell.Colspan = 2;


            return table;
        }

        private PdfPTable GenExcludeTag()
        {
            PdfPTable table = new PdfPTable(1);
            float[] widths = new float[] { 1f };
            table.TotalWidth = 16f;


            table.LockedWidth = true;
            table.SetWidths(widths);
            table.DefaultCell.Border = Rectangle.NO_BORDER;

            Phrase p = new Phrase("EXKL.", fontExcludeTag);
            PdfPCell cell = new PdfPCell(p);
            cell.Border = Rectangle.NO_BORDER;
            cell.BackgroundColor = BaseColor.BLACK;
            cell.HorizontalAlignment = Element.ALIGN_CENTER;
            table.AddCell(cell);
            cell.Padding = 0f;

            return table;
        }

        private PdfPTable GenIncludeTag()
        {
            PdfPTable table = new PdfPTable(1);
            float[] widths = new float[] { 1f };
            table.TotalWidth = 16f;


            table.LockedWidth = true;
            table.SetWidths(widths);
          //  table.DefaultCell.Border = Rectangle.NO_BORDER;

            Phrase p = new Phrase("INKL.", fontIncludeTag);
            PdfPCell cell = new PdfPCell(p);
            cell.HorizontalAlignment = Element.ALIGN_CENTER;
            table.AddCell(cell);
            cell.Padding = 0f;

            return table;
        }

        public static string BeautifyCodeId(string codeId)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < codeId.Length; i++)
            {
                if (i > 0 && i % 2 == 0)
                    sb.Append('.');
                sb.Append(codeId[i]);
            }
            return sb.ToString();
        }
 
    }

    /// <summary>
    /// A code and it's table representation
    /// </summary>
    public class CodeTableRepresentation
    {
        public CodeType Code { get; private set; }
        public PdfPTable Table { get; private set; }

        public CodeTableRepresentation(CodeType c, PdfPTable tb)
        {
            Code = c;
            Table = tb;
        }
    }

}
